// Includes
#include <iostream>

#include "../utils/cuPrintf.cu"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "../utils/Random.cu"

//Incluyo la clase global.cu  que contiene las variables y funciones globales
#include "../utils/global.cu"

//Incluyo la clase realCPU.cu para procesar las islas en CPU
#include "realCPU.cu"

//Incluyo la clase donde se calcula el fitness
#include "../fitness/realGenFitness.cu"

using namespace std;


/****************** Representacion real ********************/

/******************* Declaracion de Funciones del Host *************************/
void representacionRealGen();
void traerImprimirRealGen(float* fitnessHost, float* fitnessDevice, int tamVectorFitness, float* poblacionHost, float* poblacionDevice, int tamVectorPoblacion, int iteracion);

/******************* Declaracion de Funciones Global del Device ********************/
__global__ void inicializarPoblacionRealGen(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, float* fitnessOrdenadoDevice);
__global__ void mainKernelRealGen(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, int* poblacionActualAcambiarDevice, int*  nuevaPoblacionAcambiarDevice, float* individuosAMigrarDevice, float * probabilidadRuletaDevice, float * fitnessTotalParaRuletaDevice, struct Random r, int generacionActual, float * individuoAMigrarGPUaCPUdevice, float* fitnessOrdenadoDevice, int* vectorIndicesFitnessOrdenado);

__global__ void migracionRepresentacionRealGen(float* poblacionActualDevice, float* fitnessActualDevice, float* individuosAMigrarDevice, struct Random randomGPU, float* individuoAMigrarCPUaGPUdevice);
__global__ void individosAMigrarRepresentacionRealGen(float* nuevoFitnessDevice, float* nuevaPoblacionDevice, float* individuosAMigrarDevice, float * individuoAMigrarGPUaCPUdevice);

__global__ void reemplazoRealGen(float * poblacionActualDevice, float * nuevaPoblacionDevice, int * poblacionActualAcambiar, int * nuevaPoblacionAcambiar, float * fitnessActualDevice, float* nuevoFitnessDevice);

/******************* Declaracion de Funciones internas del Device *****************/
__device__ int obtenerMejorIndividuoRealGen(float* vectorFitness);
__device__ void obtenerPeorIndividuoRealGen(float* vectorFitness, int indices[2]);

__device__ void asignarProbabilidadRuletaRealGen(float* fitnessActualDevice, float* probabilidadRuletaReal, float* fitnessTotalParaRuletaDevice, struct Random randomGPU);

__device__ int seleccionRealGen(float* vectorFitness, float* probabilidadRuletaDevice, struct Random randomGPU, int ejecutarFuncion, int* vectorIndicesFitnessOrdenado);
__device__ int seleccionPorTorneoRealGen(float* vectorFitness, struct Random randomGPU);
__device__ int seleccionPorRangoRealGen(int* vectorIndicesFitnessOrdenado, struct Random randomGPU);
__device__ int seleccionRuedaRuletaRealGen(float* probabilidadRuletaDevice, struct Random randomGPU);

__device__ void cruzamientoRepresentacionRealGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void cruzamientoAritmeticoGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoIndividualGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoSimpleGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoCompletoGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);

__device__ void mutacionRealGen(float* poblacion, float probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual);
__device__ void mutacionUniformeGen(float* poblacion, float probabilidad, struct Random randomGPU);
__device__ void mutacionNoUniformeGen(float* poblacion, float probabilidad, struct Random randomGPU, int generacionActual);

__device__ void asignarFitnessRealGen(float* poblacion, float* vectorFitness, float* fitnessOrdenadoDevice);

__device__ void reemplazoElitistaRealGen(float* poblacionActual, float* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness);


/******************* Implementacion de Funciones del Device *****************/


__global__ void inicializarPoblacionRealGen(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, float* fitnessOrdenadoDevice){
	//Obtengo posicion a escribir.

	int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	float r = (getRandom(randomGPU, posicionGen))/100;
	poblacion[posicionGen] = r;
	
	asignarFitnessRealGen(poblacion, fitnessActualDevice, fitnessOrdenadoDevice);

}


__global__ void mainKernelRealGen(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, int* poblacionActualAcambiarDevice, int*  nuevaPoblacionAcambiarDevice, float* individuosAMigrarDevice, float * probabilidadRuletaDevice, float * fitnessTotalParaRuletaDevice, struct Random r, int generacionActual, float * individuoAMigrarGPUaCPUdevice, float* fitnessOrdenadoDevice, int* vectorIndicesFitnessOrdenado){
	if(tipoSeleccionDevice == 2){
		asignarProbabilidadRuletaRealGen(fitnessActualDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r);
	}
	int posicionCromosoma1 = seleccionRealGen(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado);
	cuPrintf("posicionCromosoma1, %d\n", posicionCromosoma1);
	int posicionCromosoma2 = seleccionRealGen(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado);
	cuPrintf("posicionCromosoma2, %d\n", posicionCromosoma2);
	cruzamientoRepresentacionRealGen(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamientoDevice, r, tipoCruzamientoDevice);

	mutacionRealGen(nuevaPoblacionDevice, probabilidadMutacionDevice, r, tipoMutacionDevice, generacionActual);
	asignarFitnessRealGen(nuevaPoblacionDevice, nuevoFitnessDevice, fitnessOrdenadoDevice);

}


__device__ void asignarProbabilidadRuletaRealGen(float* fitnessActualDevice, float* probabilidadRuletaReal, float* fitnessTotalParaRuletaDevice, struct Random randomGPU){
	__syncthreads();
	if (((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0) && (threadIdx.y == 0)){
		fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] = 0;
		for (int i = blockIdx.y*2; i < blockIdx.y*2 + cantCromosomasPoblacionDevice; i++) {
			//cuPrintf("fitnessTotalParaRuletaDevice[%d] = %f\n",blockIdx.y*2/cantCromosomasPoblacionDevice, fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice]);
			fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] += fitnessActualDevice[i];
			//cuPrintf("fitnessActualDevice[%d] = %d\n",i, fitnessActualDevice[i]);
		}
		//cuPrintf("** fitnessTotalParaRuletaDevice[%d] = %f\n", blockIdx.y*2/cantCromosomasPoblacionDevice, fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice]);
	}
	if ((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0 && threadIdx.y == 0){
		for (int i = blockIdx.y*2; i < blockIdx.y*2 + cantCromosomasPoblacionDevice; i++) {
			probabilidadRuletaReal[i] = fitnessActualDevice[i]/fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] * 100;
		}
	}

}

/******************* Implementacion de Funciones de Migracion *****************/

//Obtiene el mejor individuo de una poblacion (1 bloque por poblacion)
//selecciona el mejor elemento
__device__ int obtenerMejorIndividuoRealGen(float* vectorFitness){
	float mejor = INT_MIN;
	int indiceMejor;
	float valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness > mejor) {
			mejor = valorFitness;
			indiceMejor = i;
		}
	}
	int posicionCromosomaMejor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor * cantGenesEnCromosomaDevice;
	return posicionCromosomaMejor;
}

//Obtiene el indice del peor individuo y del fitness del peor individuo de una poblacion (1 bloque por poblacion) y retorna por referencia en el arreglo indices
__device__ void obtenerPeorIndividuoRealGen(float* vectorFitness, int indices[2]){
	float peor = INT_MAX;
	int indicePeor;
	float valorFitness;
	int posicionCromosomaPeor;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness < peor) {
			peor = valorFitness;
			indicePeor = i;
		}
	}
	posicionCromosomaPeor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indicePeor * cantGenesEnCromosomaDevice;
	indices[0] = posicionCromosomaPeor;
	indices[1] = indicePeor;
}

__global__ void migracionRepresentacionRealGen(float* poblacionActualDevice, float* fitnessActualDevice, float* individuosAMigrarDevice, struct Random randomGPU, float* individuoAMigrarCPUaGPUdevice){
	int indices[2];
	obtenerPeorIndividuoRealGen(fitnessActualDevice, indices);
	int posicionCromosomaEmigrante = indices[0];

	int posicionCromosomaInmigrante;
	int aux;

	if (blockIdx.y == 0) {
		aux = cantidadPoblacionesGPUDevice - 1;
	} else {
		aux = (blockIdx.y - 1) % cantidadPoblacionesGPUDevice;
	}

	cuPrintf("blockIdx.y - 1 = %d \n", blockIdx.y - 1);
	cuPrintf("aux = %d \n", aux);

	posicionCromosomaInmigrante = aux * cantGenesEnCromosomaDevice;

	if(blockIdx.y == 0 && cantidadPoblacionesCPUDevice > 0){
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			poblacionActualDevice[posicionCromosomaEmigrante + i] = individuoAMigrarCPUaGPUdevice[i];
		}
	}else{
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			poblacionActualDevice[posicionCromosomaEmigrante + i] = individuosAMigrarDevice[posicionCromosomaInmigrante + i];
		}
	}

	int posicionFitness = blockIdx.y * cantCromosomasPoblacionDevice + indices[1];
	fitnessActualDevice[posicionFitness] = calcularFitnessRealGen(poblacionActualDevice, posicionCromosomaEmigrante);

	cuPrintf("posicionCromosomaInmigrante = %d \n", posicionCromosomaInmigrante);
	cuPrintf("posicionCromosomaEmigrante = %d \n", posicionCromosomaEmigrante);
	cuPrintf("posicionFitness = %d \n", posicionFitness);
}

__global__ void individosAMigrarRepresentacionRealGen(float* nuevoFitnessDevice, float* nuevaPoblacionDevice, float* individuosAMigrarDevice, float* individuoAMigrarGPUaCPUdevice){
	//int posicionMejorIndividuo = seleccionPorRangoRealGen(nuevoFitnessDevice, 1);
	int posicionMejorIndividuo = obtenerMejorIndividuoRealGen(nuevoFitnessDevice);
	//if(cantidadPoblacionesCPUDevice > 0 && blockIdx.y == cantidadPoblacionesGPUDevice*cantCromosomasPoblacionDevice/2 - cantCromosomasPoblacionDevice/2){
	if(cantidadPoblacionesCPUDevice > 0 && blockIdx.y == cantidadPoblacionesGPUDevice - 1){
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			individuoAMigrarGPUaCPUdevice[i] = nuevaPoblacionDevice[posicionMejorIndividuo + i];
		}
	}else{
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			individuosAMigrarDevice[blockIdx.y*cantGenesEnCromosomaDevice + i] = nuevaPoblacionDevice[posicionMejorIndividuo + i];
		}
	}
}


/******************* Implementacion de Funciones de Selecccion *****************/

__device__ int seleccionRealGen(float* vectorFitness, float* probabilidadRuletaDevice, struct Random randomGPU, int ejecutarFuncion, int* vectorIndicesFitnessOrdenado){
	
	int resultado = -1;
	if(ejecutarFuncion == 0) {
		resultado = seleccionPorRangoRealGen(vectorIndicesFitnessOrdenado, randomGPU);
	}
	else {
		if(ejecutarFuncion == 1) {
			resultado = seleccionPorTorneoRealGen(vectorFitness, randomGPU);
		}else if(ejecutarFuncion == 2) {
			resultado = seleccionRuedaRuletaRealGen(probabilidadRuletaDevice, randomGPU);
		}
	}
	return resultado;
}


//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionPorRangoRealGen(int* vectorIndicesFitnessOrdenado, struct Random randomGPU){
	//cuPrintf("*** Seleccion elitista real ***\n");
	
	unsigned int indiceMejor = getRandomInt(randomGPU, blockIdx.y/cantGenesEnCromosomaDevice) % cantidadIndividuosSeleccionPorRangoDevice;
	int indiceFitness = (blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice + indiceMejor;

	int inicioPoblacion = (blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	//cuPrintf("indMejor = %d, iniPob = %d, vector[indiceFitness] = %d, vector[indiceFitness] * cantGenesEnCromosomaDevice = %d\n", indiceMejor, inicioPoblacion, vectorIndicesFitnessOrdenado[indiceFitness], vectorIndicesFitnessOrdenado[indiceFitness] * cantGenesEnCromosomaDevice);
	cuPrintf("indiceMejor = %d\n", indiceMejor);
	cuPrintf("vectorIndicesFitnessOrdenado[%d] = %d\n", indiceFitness, vectorIndicesFitnessOrdenado[indiceFitness]);
	int posicionCromosoma = inicioPoblacion + vectorIndicesFitnessOrdenado[indiceFitness] * cantGenesEnCromosomaDevice;
	return posicionCromosoma;	
}

//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoRealGen(float* vectorFitness, struct Random randomGPU){
	//cuPrintf("*** Seleccion por torneo real ***\n");
	
	unsigned int r = getRandomInt(randomGPU, blockIdx.y/cantGenesEnCromosomaDevice)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * 2 + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantGenesEnCromosomaDevice * 2 + r * cantGenesEnCromosomaDevice;
	
	if (blockIdx.y % cantCromosomasPoblacionDevice != 0) {
		posicionFitnessAleatoria1 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) + r;
		posicionCromosomaAleatoria1 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) * cantGenesEnCromosomaDevice * 2  + r * cantGenesEnCromosomaDevice;
	}
	
	r = getRandomInt(randomGPU, blockIdx.y/cantGenesEnCromosomaDevice + 1) % cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * 2 + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantGenesEnCromosomaDevice * 2 + r * cantGenesEnCromosomaDevice;

	if (blockIdx.y % cantCromosomasPoblacionDevice != 0) {
		posicionFitnessAleatoria2 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) + r;
		posicionCromosomaAleatoria2 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) * cantGenesEnCromosomaDevice * 2  + r * cantGenesEnCromosomaDevice;
	}
	
	if (vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
	
}

//Seleccion por rueda de ruleta
__device__ int seleccionRuedaRuletaRealGen(float* probabilidadRuletaDevice, struct Random randomGPU){
	float aux = getRandomInt(randomGPU, blockIdx.y*cantGenesEnCromosomaDevice);
	float r = fmod(aux, 100);
	cuPrintf("r, %f\n", r);
	float suma = probabilidadRuletaDevice[(blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice];
	cuPrintf("suma %f\n", suma);
	int i = 0;
	while(r>suma){
		i++;
		suma = suma + probabilidadRuletaDevice[(blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice + i];
	}
	cuPrintf("i %d\n", i);
	//return blockIdx.y * cantGenesEnCromosomaDevice * 2 + i * cantGenesEnCromosomaDevice;
	return (blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + i * cantGenesEnCromosomaDevice;
}


/******************* Implementacion de Funciones de Cruzamiento *****************/

__device__ void cruzamientoRepresentacionRealGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0) {
		cruzamientoAritmeticoGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			cruzamientoAritmeticoIndividualGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		}
		else {
			if(ejecutarFuncion == 2) {
				cruzamientoAritmeticoSimpleGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
			else {
				if(ejecutarFuncion == 3) {
					cruzamientoAritmeticoCompletoGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
				}
			}
		}
	}
}


__device__ void cruzamientoAritmeticoGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	//cuPrintf("*** Cruzamiento Aritmetico ***\n");
	int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int prob = (getRandomInt(randomGPU, blockIdx.y) % 101);

	//cuPrintf("prob mutacion = %d\n", prob);
	if (prob < probabilidad){
		float alfa = fmod(getRandom(randomGPU,  blockIdx.y), 1);
		//cuPrintf("alfa = %f\n", alfa);
		if (threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma1 + threadIdx.y] + (1-alfa) * poblacionActual[posicionCromosoma2 + threadIdx.y];
		}else{
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice] + (1-alfa) * poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
	else{
		if (threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}
		else {
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}

	}
}

__device__ void cruzamientoAritmeticoIndividualGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Individual ***\n");
	int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;

	unsigned int prob = (getRandomInt(randomGPU, blockIdx.y) % 101);

	//cuPrintf("prob mutacion = %d\n", prob);
	if (prob < probabilidad){
		float alfa = fmod(getRandom(randomGPU,  blockIdx.y), 1);
		int genAcruzar = (getRandomInt(randomGPU, blockIdx.y) % cantGenesEnCromosomaDevice);
		cuPrintf("alfa = %f\n", alfa);
		cuPrintf("genAcruzar = %d\n", genAcruzar);
		if (threadIdx.y < cantGenesEnCromosomaDevice && threadIdx.y == genAcruzar){
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma2 + threadIdx.y] + (1-alfa) * poblacionActual[posicionCromosoma1 + threadIdx.y];
		}else if(threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y];
		}else if(threadIdx.y - cantGenesEnCromosomaDevice == genAcruzar){
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice] + (1-alfa) * poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}else{
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
	else{
		if (threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}
		else {
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
}

__device__ void cruzamientoAritmeticoSimpleGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Simple ***\n");
	int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int prob = (getRandomInt(randomGPU, blockIdx.y) % 101);

	//cuPrintf("prob mutacion = %d\n", prob);
	if (prob < probabilidad){
		float alfa = fmod(getRandom(randomGPU,  blockIdx.y), 1);
		int k = (getRandomInt(randomGPU, blockIdx.y) % cantGenesEnCromosomaDevice);
		cuPrintf("alfa = %f\n", alfa);
		cuPrintf("k = %d\n", k);
		if (threadIdx.y < cantGenesEnCromosomaDevice && threadIdx.y >= k){
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma2 + threadIdx.y] + (1-alfa) * poblacionActual[posicionCromosoma1 + threadIdx.y];
		}else if(threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}else if(threadIdx.y - cantGenesEnCromosomaDevice >= k){
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice] + (1-alfa) * poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}else{
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
	else{
		if (threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}
		else {
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
}

__device__ void cruzamientoAritmeticoCompletoGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Completo ***\n");
	int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int prob = (getRandomInt(randomGPU, blockIdx.y) % 101);

	//cuPrintf("prob mutacion = %d\n", prob);
	if (prob < probabilidad){
		float alfa = fmod(getRandom(randomGPU,  blockIdx.y), 1);
		cuPrintf("alfa = %f\n", alfa);
		if (threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma1 + threadIdx.y] + (1-alfa) * poblacionActual[posicionCromosoma2 + threadIdx.y];
		}else{
			nuevaPoblacion[posicionGen] = alfa * poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice] + (1-alfa) * poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
	else{
		if (threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}
		else {
			nuevaPoblacion[posicionGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
}


/******************* Implementacion de Funciones de Mutacion *****************/


__device__ void mutacionRealGen(float* poblacion, float probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual){

	if(ejecutarFuncion == 0) {
		mutacionUniformeGen(poblacion, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			mutacionNoUniformeGen(poblacion, probabilidad, randomGPU, generacionActual);
		}
	}
}


__device__ void mutacionUniformeGen(float* poblacion, float probabilidad, struct Random randomGPU){
	//cuPrintf("*** Mutacion uniforme real ***\n");

	int posicionNuevoGen1 = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int bloque = blockIdx.y;
	unsigned int r;

	if (threadIdx.y > cantGenesEnCromosomaDevice){
		bloque++;
	}
	r = (getRandomInt(randomGPU, bloque) % cantGenesEnCromosomaDevice);
	
	float left_k, right_k, aux1;
	float aux;
	if((threadIdx.y % cantGenesEnCromosomaDevice) == r){
		//cuPrintf("r = %d \n",r);
		if ((threadIdx.y % cantGenesEnCromosomaDevice) == 0){
			left_k = poblacion[posicionNuevoGen1 + cantGenesEnCromosomaDevice - 1];
		}else{
			left_k = poblacion[posicionNuevoGen1 - 1];
		}
		
		if ((threadIdx.y % cantGenesEnCromosomaDevice) == (cantGenesEnCromosomaDevice - 1)){
			right_k = poblacion[posicionNuevoGen1 - cantGenesEnCromosomaDevice + 1];
		}else{
			right_k = poblacion[posicionNuevoGen1 + 1];
		}
		
		aux = getRandom(randomGPU, posicionNuevoGen1);
		//cuPrintf("poblacion[posicionNuevoGen1] = %f \n",poblacion[posicionNuevoGen1]);
		//cuPrintf("left_k = %f \n",left_k);
		//cuPrintf("right_k = %f \n",right_k);
		//cuPrintf("aux = %f \n",aux);
		if (left_k > right_k){
			aux1 = right_k;
			right_k = left_k;
			left_k = aux1;
		}
		poblacion[posicionNuevoGen1] = left_k + aux * (right_k - left_k);
	}
}

__device__ void mutacionNoUniformeGen(float* poblacion, float probabilidad, struct Random randomGPU, int generacionActual){
	cuPrintf("*** Mutacion no uniforme real ***\n");
	
	int posicionNuevoGen1 = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int bloque = blockIdx.y;
	unsigned int r;

	if (threadIdx.y > cantGenesEnCromosomaDevice){
		bloque++;
	}
	r = (getRandomInt(randomGPU, bloque) % cantGenesEnCromosomaDevice);

	float left_k, right_k, resultado;
	int aux;
	if((threadIdx.y % cantGenesEnCromosomaDevice) == r){
		cuPrintf("r = %d \n",r);
		if ((threadIdx.y % cantGenesEnCromosomaDevice) == 0){
			left_k = poblacion[posicionNuevoGen1 + cantGenesEnCromosomaDevice - 1];
		}else{
			left_k = poblacion[posicionNuevoGen1 - 1];
		}

		if ((threadIdx.y % cantGenesEnCromosomaDevice) == (cantGenesEnCromosomaDevice - 1)){
			right_k = poblacion[posicionNuevoGen1 - cantGenesEnCromosomaDevice + 1];
		}else{
			right_k = poblacion[posicionNuevoGen1 + 1];
		}

		aux = getRandomInt(randomGPU, posicionNuevoGen1) % 2;
		cuPrintf("poblacion[posicionNuevoGen1] = %f \n",poblacion[posicionNuevoGen1]);
		cuPrintf("left_k = %f \n",left_k);
		cuPrintf("right_k = %f \n",right_k);
		cuPrintf("aux = %d \n",aux);
		if (aux == 0){
			resultado = poblacion[posicionNuevoGen1] + ((right_k - poblacion[posicionNuevoGen1]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidadDevice)));
		}
		else {
			resultado = poblacion[posicionNuevoGen1] - ((poblacion[posicionNuevoGen1] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidadDevice)));
		}
		poblacion[posicionNuevoGen1] = resultado;
	}

}

/******************* Implementacion de Funciones de fitness *****************/

__device__ void asignarFitnessRealGen(float* poblacion, float* vectorFitness, float* fitnessOrdenadoDevice){
	__syncthreads();
	if (threadIdx.y % cantGenesEnCromosomaDevice == 0){
		int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
		int posicionFitness = blockIdx.y*2 + threadIdx.y/cantGenesEnCromosomaDevice;
		//cuPrintf("posicionFitness, %d\n", posicionFitness);
		vectorFitness[posicionFitness] = 0;
		for (int i = posicionGen; i < posicionGen + cantGenesEnCromosomaDevice; i++) {
			vectorFitness[posicionFitness] = vectorFitness[posicionFitness] + poblacion[i];
			fitnessOrdenadoDevice[posicionFitness] = vectorFitness[posicionFitness];
		}
	}
}


/******************** Implementacion de Funciones de reemplazo **************/

__global__ void reemplazoRealGen(float * poblacionActualDevice, float * nuevaPoblacionDevice, int * poblacionActualAcambiar, int * nuevaPoblacionAcambiar, float * fitnessActualDevice, float* nuevoFitnessDevice){
	if(tipoReemplazoDevice == 1) {
		reemplazoElitistaRealGen(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiar, nuevaPoblacionAcambiar, fitnessActualDevice, nuevoFitnessDevice);
	}
}

__device__ void reemplazoElitistaRealGen(float* poblacionActual, float* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness) {
	//si es el primer bloque de alguna poblacion y es el primer hilo de ese bloque entonces reemplazo
	if (((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0) && (threadIdx.y == 0)){
		int cantAremplazar = porcentajeElitismoDevice*cantCromosomasPoblacionDevice/100;
		float peor = INT_MAX;
		float mejor = INT_MIN;
		int posPeor = -1;
		int posMejor = -1;
		int posFitness = blockIdx.y / 2 * cantCromosomasPoblacionDevice;
		//este for se encarga de inicializar los primeros cantAreemplazar valores de la estructura auxiliar poblacionActualAcambiar en 1 y guarda su peor valor para luego poder obtener los peores canAReemplazar valores
		//tambien inicializa los primeros canAreemplazar valores de nuevaPoblacionACambiar en 1 y guarda su mejor valor
		for (int i = 0; i < cantAremplazar; i++){
			if (fitnessActual[posFitness + i] < peor) {
				peor = fitnessActual[posFitness + i];
				posPeor = i;
			}

			if (nuevoFitness[posFitness + i] > mejor) {
				mejor = nuevoFitness[posFitness + i];
				posMejor = i;
			}

			poblacionActualAcambiar[posFitness + i] = 1;
			nuevaPoblacionAcambiar[posFitness + i] = 1;
		}

		//esta iteracion se encarga de obtener los cantAremplazar peores valores de la nueva poblacion y los mejores cantAremplazar valores de la poblacion actual
		for (int i = cantAremplazar; i < cantCromosomasPoblacionDevice; i++){
			if (fitnessActual[posFitness + i] > peor) {
				poblacionActualAcambiar[posFitness + i] = 1;
				poblacionActualAcambiar[posFitness + posPeor] = 0;
				peor = INT_MAX;
				posPeor = -1;
				for (int j = 0; j <= i; j++){
					if (poblacionActualAcambiar[posFitness + j] == 1) {
						if (fitnessActual[posFitness + j] < peor) {
							peor = fitnessActual[posFitness + j];
							posPeor = j;
						}
					}
				}
			}
			else {
				poblacionActualAcambiar[posFitness + i] = 0;
			}

			if (nuevoFitness[posFitness + i] < mejor) {
				nuevaPoblacionAcambiar[posFitness + i] = 1;
				nuevaPoblacionAcambiar[posFitness + posMejor] = 0;
				mejor = INT_MIN;
				posMejor = -1;
				for (int j = 0; j <= i; j++) {
					if (nuevaPoblacionAcambiar[posFitness + j] == 1) {
						if (nuevoFitness[posFitness + j] > mejor) {
							mejor = nuevoFitness[posFitness + j];
							posMejor = j;
						}
					}
				}
			}
			else {
				nuevaPoblacionAcambiar[posFitness + i] = 0;
			}
		}

		//esta iteracion se encarga de reemplazar los mejores cantAremplazar individuos de la poblacion actual por los cantAremplazar peores individuos de la nueva poblacion
		for (int i = 0; i < cantAremplazar; i++) {
			int j = 0;
			while (poblacionActualAcambiar[posFitness + j] == 0) {
				j++;
			}

			int k = 0;
			while (nuevaPoblacionAcambiar[posFitness + k] == 0) {
				k++;
			}

			nuevoFitness[posFitness + k] = fitnessActual[posFitness + j];
			int posNuevoCromosoma = blockIdx.y * 2 * cantGenesEnCromosomaDevice + k * cantGenesEnCromosomaDevice;
			int posCromosomaActual = blockIdx.y * 2 * cantGenesEnCromosomaDevice + j * cantGenesEnCromosomaDevice;
			for (int h = 0; h < cantGenesEnCromosomaDevice; h++) {
				nuevaPoblacion[posNuevoCromosoma + h] = poblacionActual[posCromosomaActual + h];
			}
			poblacionActualAcambiar[posFitness + j] = 0;
			nuevaPoblacionAcambiar[posFitness + k] = 0;
		}
	}
}
/******************* Implementacion de Funciones del host *****************/

void traerImprimirRealGen(float* fitnessHost, float* fitnessDevice, int tamVectorFitness, float* poblacionHost, float* poblacionDevice, int tamVectorPoblacion, int iteracion){
	cudaMemcpy (fitnessHost, fitnessDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (poblacionHost, poblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "\nPOBLACION ITERACION "<< iteracion<<" GPU\n";
	for (int i = 0; i<totalGenesGPU; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%f ", poblacionHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
		printf("%f ", fitnessHost[i]);
	}
	cout << "\n\n\n\n";
}

void representacionRealGen() {
	int tamVectorPoblacion = sizeof(float)*totalGenesGPU;
	int tamVectorFitness = sizeof(float)*totalIndividuosGPU;
	int tamVectorIndividuosAMigrar = sizeof(float)*cantGenesEnCromosoma*cantidadPoblacionesGPU;
	int tamVectorFitnessFloat = sizeof(float)*totalIndividuosGPU;
	int tamVectorPoblacionParaRuleta = sizeof(float)*cantidadPoblacionesGPU;

	int tamVectorIndividuosAMigrarGPU = sizeof(float)*cantGenesEnCromosoma*cantidadPoblacionesGPU;
	int tamVectorIndividuosAMigrarCPU = sizeof(float)*cantGenesEnCromosoma*cantidadPoblacionesCPU;
	int tamVectorPoblacionParaRuletaCPU = sizeof(float)*totalIndividuosCPU;
	int tamVectorFitnessFloatCPU = sizeof(float)*cantidadPoblacionesCPU;

	static float *poblacionActualHostGPU = (float *) malloc(tamVectorPoblacion);
	static float *nuevaPoblacionHostGPU = (float *) malloc(tamVectorPoblacion);
	static float *fitnessActualHostGPU = (float *) malloc(tamVectorFitness);
	static float *nuevoFitnessHostGPU = (float *) malloc(tamVectorFitness);
	static float *probabilidadRuletaHostGPU = (float *) malloc(tamVectorFitnessFloat);
	static float *fitnessTotalParaRuletaHost = (float *) malloc(tamVectorPoblacionParaRuleta);
	static float *individuosAMigrarHostGPU = (float *) malloc(tamVectorIndividuosAMigrar);
//SE BORRA//
	static int *indicesFitnessOrdenadoHost = (int *) malloc(tamVectorFitness);
////////////
	float *poblacionActualDevice;
	float *nuevaPoblacionDevice;
	float *fitnessActualDevice;
	float *nuevoFitnessDevice;
	float * fitnessOrdenadoDevice;
	int * indicesFitnessOrdenadoDevice;
	float *individuosAMigrarDevice;
	float *probabilidadRuletaDevice;
	float *fitnessTotalParaRuletaDevice;
	int * poblacionActualAcambiarDevice;
	int * nuevaPoblacionAcambiarDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitness);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitness);
	cudaMalloc(&indicesFitnessOrdenadoDevice, tamVectorFitness);
	cudaMalloc(&fitnessOrdenadoDevice, tamVectorFitness);
	cudaMalloc(&probabilidadRuletaDevice, tamVectorPoblacionParaRuleta);
	cudaMalloc(&fitnessTotalParaRuletaDevice, tamVectorFitnessFloat);
	cudaMalloc(&poblacionActualAcambiarDevice, tamVectorFitness);
	cudaMalloc(&nuevaPoblacionAcambiarDevice, tamVectorFitness);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU);

	//Inicio numeros aleatorios.
	struct Random r = initRandom(cantCromosomasPoblacion*cantGenesEnCromosoma*cantidadPoblacionesGPU);

	//inicializo grid con dimension 1 x cantidadPoblacionesGPU*cantCromosomasPoblacion/2 bloques
	dim3 tamGrid(1, cantidadPoblacionesGPU*cantCromosomasPoblacion/2); //Grid dimensión
	//inicializo bloque con dimension 1 x cantGenesEnCromosoma*2 x 1 hilos
	dim3 tamBlock(1,cantGenesEnCromosoma*2,1); //Block dimensión

	//inicializo grid de individuos a migrar con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGridIndividuosAMigrar(1, cantidadPoblacionesGPU);
	//inicializo bloque de individuos a migrar con dimension 1 x cantCromosomasPoblacion/2 x 1 hilos
	dim3 tamBlockIndividuosAMigrar(1,cantCromosomasPoblacion/2,1);

	//inicializo grid de migracion con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGridMigracion(1, cantidadPoblacionesGPU);
	//inicializo bloque de migracion con 1 hilo por bloque
	dim3 tamBlockMigracion(1,1,1); //Block dimensión


	//inicializacion de la poblacion en GPU
	if(cantidadPoblacionesGPU > 0){
		inicializarPoblacionRealGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, fitnessOrdenadoDevice);
		
		//si la selección es por rango
		if (tipoSeleccion == 0) {
			ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
		}
		
		traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, -1);
	}
	
	static float *individuosAMigrarHostCPU;
	MersenneTwister* mt;
	int tamVectorPoblacionCPU;
	int tamVectorFitnessCPU;
	static float *poblacionActualHostCPU;
	static float *nuevaPoblacionHostCPU;
	static float *fitnessActualHostCPU;
	static float *nuevoFitnessHostCPU;
	static int *indicesFitnessOrdenadoHostCPU;
	static float * fitnessOrdenadoHostCPU;
	int * poblacionActualAcambiarHostCPU;
	int * nuevaPoblacionAcambiarHostCPU;
	float * probabilidadRuletaHostCPU;
	float * fitnessTotalParaRuletaHostCPU;

	if(cantidadPoblacionesCPU > 0){

		tamVectorPoblacionCPU = sizeof(float)*totalGenesCPU;
		tamVectorFitnessCPU = sizeof(float)*totalIndividuosCPU;

		poblacionActualHostCPU = (float *) malloc(tamVectorPoblacionCPU);
		nuevaPoblacionHostCPU = (float *) malloc(tamVectorPoblacionCPU);
		fitnessActualHostCPU = (float *) malloc(tamVectorFitnessCPU);
		nuevoFitnessHostCPU = (float *) malloc(tamVectorFitnessCPU);
		individuosAMigrarHostCPU = (float *) malloc(tamVectorIndividuosAMigrarCPU);
		indicesFitnessOrdenadoHostCPU = (int *) malloc(tamVectorFitness);
		fitnessOrdenadoHostCPU = (float *) malloc(tamVectorFitness);
		poblacionActualAcambiarHostCPU = (int *) malloc(tamVectorFitness);
		nuevaPoblacionAcambiarHostCPU = (int *) malloc(tamVectorFitness);
		probabilidadRuletaHostCPU = (float *) malloc(tamVectorPoblacionParaRuletaCPU);
		fitnessTotalParaRuletaHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);

		//////////INICIALIZO POBLACION EN CPU
		//inicializo la clase mt para las variables aleatorias

		//Inicio numeros aleatorios.
		mt = new MersenneTwister();
		mt->init_genrand(time(NULL));

		//inicializacion de la poblacion en GPU
		inicializarPoblacionRealCPU(poblacionActualHostCPU, fitnessActualHostCPU, mt, cantCromosomasPoblacion, fitnessOrdenadoHostCPU);
		imprimirRealCPU(fitnessActualHostCPU, tamVectorFitnessCPU, poblacionActualHostCPU, tamVectorPoblacionCPU, -1);

		//si la selección es por rango
		if (tipoSeleccion == 0) {
			for (int i = 0; i < cantidadPoblacionesCPU; i++) {
				ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
			}
		}
	}

	int tamVectorIndividuoAMigrarCPUGPU;
	static float *individuoAMigrarCPUaGPUhost;
	static float *individuoAMigrarGPUaCPUhost;
	static float *individuoAMigrarCPUaGPUdevice;
	static float *individuoAMigrarGPUaCPUdevice;
	if(cantidadPoblacionesCPU > 0 && cantidadPoblacionesGPU > 0){
		tamVectorIndividuoAMigrarCPUGPU = sizeof(float)*cantGenesEnCromosoma;
		cudaMalloc(&individuoAMigrarCPUaGPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		cudaMalloc(&individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarCPUaGPUhost = (float *) malloc(tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarGPUaCPUhost = (float *) malloc(tamVectorIndividuoAMigrarCPUGPU);
	}

	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){
		if(cantidadPoblacionesGPU > 0){
			//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
			if ((iteracion % 2) == 0) {
				
				if ((tipoSeleccion == 0) && (modoDebug == 1)) {
					cudaMemcpy (indicesFitnessOrdenadoHost, indicesFitnessOrdenadoDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
					cout << "\n\n\nINDICES GPU\n";
					for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
						printf("%d ", indicesFitnessOrdenadoHost[i]);
					}
					cout << "\n\n\n\n";
				}
				
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelRealGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, individuosAMigrarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, iteracion, individuoAMigrarGPUaCPUdevice, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				
				//traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, 333);
				
				reemplazoRealGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, fitnessActualDevice, nuevoFitnessDevice);

				//si la selección es por rango
				if (tipoSeleccion == 0) {
					ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}
				
				if (modoDebug == 1) {
					traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
				}
			} else {//sino invoco a GPU con nuevaPoblacion, poblacionActual
				
				if ((tipoSeleccion == 0) && (modoDebug == 1)) {
					cudaMemcpy (indicesFitnessOrdenadoHost, indicesFitnessOrdenadoDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
					cout << "\n\n\nINDICES GPU\n";
					for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
						printf("%d ", indicesFitnessOrdenadoHost[i]);
					}
					cout << "\n\n\n\n";
				}
				
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelRealGen<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, individuosAMigrarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, iteracion, individuoAMigrarGPUaCPUdevice, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				
				//traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, 222);
				
				reemplazoRealGen<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, nuevoFitnessDevice, fitnessActualDevice);

				//si la selección es por rango
				if (tipoSeleccion == 0) {
					ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}
				
				if (modoDebug == 1) {
					traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
				}
			}
		}
		//Tiro islas en CPU
		if(cantidadPoblacionesCPU > 0) {
			if ((tipoSeleccion == 0) && (modoDebug == 1)) {
				cout << "\n\n\nINDICES CPU\n";
				for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesCPU; i++){
					printf("%d ", indicesFitnessOrdenadoHostCPU[i]);
				}
				cout << "\n\n\n\n";
			}
			representacionRealCPU(poblacionActualHostCPU, nuevaPoblacionHostCPU, fitnessActualHostCPU, nuevoFitnessHostCPU, poblacionActualAcambiarHostCPU, nuevaPoblacionAcambiarHostCPU, individuosAMigrarHostCPU, mt, iteracion, tamVectorPoblacion, tamVectorFitness, individuoAMigrarCPUaGPUhost, fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, probabilidadRuletaHostCPU, fitnessTotalParaRuletaHostCPU);

			//si la selección es por rango
			if (tipoSeleccion == 0) {
				for (int i = 0; i < cantidadPoblacionesCPU; i++) {
					ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
				}
			}
		}
		//Si hay que migrar
		if ((iteracion + 1) % generacionesMigrar == 0) {

			if(cantidadPoblacionesGPU > 0){
				if ((iteracion % 2) == 0) {
					individosAMigrarRepresentacionRealGen<<<tamGridIndividuosAMigrar, tamBlockIndividuosAMigrar>>>(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
				} else {
					individosAMigrarRepresentacionRealGen<<<tamGridIndividuosAMigrar, tamBlockIndividuosAMigrar>>>(fitnessActualDevice, poblacionActualDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
				}
			}

			if(cantidadPoblacionesGPU > 0 && cantidadPoblacionesCPU > 0){
				cudaMemcpy (individuoAMigrarCPUaGPUdevice, individuoAMigrarCPUaGPUhost, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyHostToDevice);
				cudaMemcpy (individuoAMigrarGPUaCPUhost, individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyDeviceToHost);
				if (modoDebug == 1) {
					/**  Imprimir individuos a migrar **/
					cudaMemcpy (individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);

					printf("----individuosAMigrar GPU----\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++) {
						printf("%f ", individuosAMigrarHostGPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n---------------------------\n");


					printf("----individuo a migrar de GPU a CPU----\n");
					for (int i = 0; i<cantGenesEnCromosoma; i++) {
						printf("%f ", individuoAMigrarGPUaCPUhost[i]);
					}
					printf("\n-------------------------------------\n");

					printf("________individuosAMigrar CPU______\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++) {
						printf("%f ", individuosAMigrarHostCPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n__________________________________\n");

					printf("_____individuo a migrar de CPU a GPU_____\n");
					for (int i = 0; i<cantGenesEnCromosoma; i++) {
						printf("%f ", individuoAMigrarCPUaGPUhost[i]);
					}
					printf("\n________________________________________\n");

					/** ************* **/
				}
				if ((iteracion % 2) == 0) {
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n**Despues de Migrar GPU**";
						traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
					}
					migracionRepresentacionRealCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirRealCPU(nuevoFitnessHostCPU, tamVectorFitness, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
					}
				} else {
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n**Despues de Migrar GPU**";
						traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
					}
					migracionRepresentacionRealCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirRealCPU(fitnessActualHostCPU, tamVectorFitness, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
					}
				}
			}else if(cantidadPoblacionesCPU > 0){
				//poblaciones solamente en CPU
				if ((iteracion % 2) == 0) {
					migracionRepresentacionRealCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirRealCPU(nuevoFitnessHostCPU, tamVectorFitness, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
					}
				} else {
					migracionRepresentacionRealCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirRealCPU(fitnessActualHostCPU, tamVectorFitness, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
					}
				}
				if (modoDebug == 1) {
					printf("individuosAMigrar CPU\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++) {
						printf("%f ", individuosAMigrarHostCPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n-----------------\n");
				}
			} else {
				//poblaciones solamente en GPU
				if (modoDebug == 1) {
					/**  Imprimir individuos a migrar **/
					cudaMemcpy (individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);

					printf("individuosAMigrar GPU\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++) {
						printf("%f ", individuosAMigrarHostGPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n-----------------\n");
					/** ************* **/
				}
				if ((iteracion % 2) == 0) {
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar **";
						traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
					}
				} else {
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar **";
						traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
					}
				}
			}
		}
		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	if (cantidadPoblacionesGPU > 0) {
		if (((iteracion -1) % 2) == 0) {
			traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion,INT_MAX);
		} else {
			traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion,INT_MAX);
		}
	}

	// liberar memoria de las matrices en el device
	cudaFree(fitnessActualDevice);
	cudaFree(poblacionActualDevice);
	cudaFree(nuevoFitnessHostGPU);
	cudaFree(nuevaPoblacionDevice);
}

